#pragma once

#include "util.hpp"
#include <iostream>
#include <sstream>
#include <cassert>
#include <fstream>
#include <memory>

namespace iceLogs
{
    class logSink
    {
        public:
        using ptr = std::shared_ptr<logSink>;
            logSink(){}
            virtual ~logSink(){}
            virtual void log(const char* data, size_t len) = 0;

    };
    //落地方向：标准输出
    class StdoutSink : public logSink 
    {
        public:
        //将日志写入到标准输出
            void log(const char* data, size_t len)
            {
                //std::cout << data << std::endl; 这种方式有问题，以\0为结尾的字符串，而我们输出的消息中可能有\0
                std::cout.write(data, len);
            }
    };

    //落地方向：指定文件
    class FileSink : public logSink
    {
        public:
            //构造时传入文件名， 并打开文件，将操作句柄管理起来
            FileSink(const std::string& pathname):_pathname(pathname)
            {
                //1.创建文件目录
                util::File::createDirectory(util::File::path(pathname));
                

            }
            //将日志写入到指定文件中
            void log(const char* data, size_t len)
            {
                //2.打开文件
                _ofs.open(_pathname, std::ios::binary | std::ios::app);
                //判断文件是否打开
                assert(_ofs.is_open());
                _ofs.write(data, len);
                if (_ofs.good() == false) {
                    std::cout << "日志输出文件失败！\n";
                }
                _ofs.close();
            }
        private:
            std::string _pathname;
            std::ofstream _ofs;

    };
    //落地方向：滚动文件
    class RollBySizeSink : public logSink
    {
        public:
            //构造时传入文件名， 并打开文件，将操作句柄管理起来
            RollBySizeSink(const std::string& basename, size_t max_fsize)
            :_basename(basename)
            ,_Max_fsize(max_fsize)
            ,_cur_fsize(0)
            ,name_count(0)
            {
                std::string pathname = createNewFile();
                //1.创建文件目录
                util::File::createDirectory(util::File::path(pathname));
                //2.打开文件
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                //判断文件是否打开
                assert(_ofs.is_open());
            }
            //将日志写入到指定文件中, 写入前判断文件的大小，文件大小超过上限时切换文件
            void log(const char* data, size_t len)
            {
                if(_cur_fsize >= _Max_fsize)
                {
                    _cur_fsize = 0;
                    _ofs.close();
                    //1.创建新文件
                    std::string pathname = createNewFile();
                    //2.打开文件
                    _ofs.open(pathname, std::ios::binary | std::ios::app);
                    //判断文件是否打开
                    assert(_ofs.is_open());
                }
                _ofs.write(data, len);
                assert(_ofs.good());
                _cur_fsize += len;
            }
        private:
            std::string createNewFile()//创建新文件
            {
                std::stringstream filename;
                time_t now = util::Date::getTime();
                struct tm t;
                localtime_r(&now, &t);

                filename << _basename;
                filename << t.tm_year + 1900;
                filename << t.tm_mon + 1;
                filename << t.tm_mday;
                filename << t.tm_hour;
                filename << t.tm_min;
                filename << t.tm_sec;
                filename << "-";
                filename << name_count++;
                filename << ".log";
                return filename.str();
            }
        private:
            size_t name_count;
            std::string _basename;
            std::ofstream _ofs;
            size_t _Max_fsize;    //记录文件可以存储的最大值
            size_t _cur_fsize;    //记录文件当前的存储值
    };

    //落地方向：以时间划分的滚动文件
    class RollByTime : public logSink
    {
        enum
        {
            SEC_GAP = 1,
            MINGAP,
            HOURGAP,
            DAYGAP
        };
        public:
            RollByTime(const std::string& basename)
            :_basename(basename)
            ,_oldtime(iceLogs::util::Date::getTime())
            {
                std::string pathname = createNewFile();
                //1.创建文件目录
                util::File::createDirectory(util::File::path(pathname));
                //2.打开文件
                _ofs.open(pathname, std::ios::binary | std::ios::app);
                //判断文件是否打开
                assert(_ofs.is_open());
            }

            void log(const char* data, size_t len)
            {
                time_t now = iceLogs::util::Date::getTime();
                if(_oldtime % now >= SEC_GAP)
                {
                    _oldtime = now;
                    _ofs.close();
                    //1.创建新文件
                    std::string pathname = createNewFile();
                    //2.打开文件
                    _ofs.open(pathname, std::ios::binary | std::ios::app);
                    //判断文件是否打开
                    assert(_ofs.is_open());

                }
                _ofs.write(data, len);
                assert(_ofs.good());
            }
        private:
            std::string createNewFile()//创建新文件
            {
                std::stringstream filename;
                
                struct tm t;
                localtime_r(&_oldtime, &t);
                filename << _basename;
                filename << t.tm_year + 1900;
                filename << t.tm_mon + 1;
                filename << t.tm_mday;
                filename << t.tm_hour;
                filename << t.tm_min;
                filename << t.tm_sec;
                filename << ".log";
                return filename.str();
            }
        private:
            std::string _basename;
            std::ofstream _ofs;
            time_t _oldtime;

    };

    class SinkFactory
    {
        public:
            template <typename SinkType, typename ...Args>  
            static logSink::ptr create(Args &&...args)
            {
                return std::make_shared<SinkType>(std::forward<Args>(args)...);
            }
    };
}